import React from 'react';
import ReactDOM from 'react-dom';

import styles from './index.less';
import {
  set_child_offsets,
  defaultData,
  set_child_sizes,
  set_node_size,
  set_node_offset,
  img_stop_propagation_events,
  arr_equals,
  compute_text_size,
  compute_node_size,
} from './utils';
import {
  NODE_TEXT_PADDING,
  LINE_WIDTH,
  Data,
  Offset,
  Size,
  MIN_DIS_SIB,
  DIS_PARENT_CHILDREN,
  OffsetChangedType,
} from './common';

/**
 * offset: 该节点相对于父节点的位移
 **/
export const NodePath = (props: { offset: Offset }) => {
  const offset = props.offset;

  const x1 = 0;
  const y1 = offset.y < 0 ? -offset.y + LINE_WIDTH / 2 : LINE_WIDTH / 2;
  const x2 = offset.x;
  const y2 = offset.y < 0 ? 0 + LINE_WIDTH / 2 : offset.y + LINE_WIDTH / 2;
  const cx1 = x1 + 20;
  const cy1 = y1;
  const cx2 = x2 - 20;
  const cy2 = y2;
  return (
    <svg
      style={offset.y < 0 ? { top: `calc(100%)` } : { bottom: -LINE_WIDTH }}
      width={Math.abs(offset.x)}
      height={Math.abs(offset.y) + LINE_WIDTH}
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        d={`M${x1} ${y1} C ${cx1} ${cy1} ${cx2} ${cy2} ${x2} ${y2}`}
        stroke="gray"
        strokeWidth="2"
        fill="transparent"
      />
    </svg>
  );
};

export class MindNode extends React.Component<
  {
    path: string[];
    data: Data;
    offset: Offset;
    is_root?: boolean;
    scale: number;
    events: { [name: string]: Function };
    notify_global_offset_changed: Function;
    img_on_click: Function;
    comment_on_click: Function;
  },
  {}
> {
  shouldComponentUpdate(nextProps, nextState) {
    const equal = this.props.data.equals(nextProps.data);
    const offsetChanged =
      this.props.offset.x !== nextProps.offset.x ||
      this.props.offset.y !== nextProps.offset.y;
    const shouldUpdate =
      !equal ||
      offsetChanged ||
      nextProps.scale !== this.props.scale ||
      !arr_equals(nextProps.path, this.props.path);
    return shouldUpdate;
  }

  componentDidUpdate() {
    const id = this.props.data.get('id');
    this.props.notify_global_offset_changed(
      OffsetChangedType.change,
      this.props.path,
      this.props.offset,
      compute_text_size(this.props.data.get('name')),
    );
    set_child_offsets(id, this.child_offsets);
    set_child_sizes(id, this.child_sizes);
    set_node_size(id, this.node_size);
    set_node_offset(id, this.props.offset);
  }

  componentDidMount() {
    const id = this.props.data.get('id');
    this.props.notify_global_offset_changed(
      OffsetChangedType.mount,
      this.props.path,
      this.props.offset,
      compute_text_size(this.props.data.get('name')),
    );
    set_child_offsets(id, this.child_offsets);
    set_child_sizes(id, this.child_sizes);
    set_node_size(id, this.node_size);
    set_node_offset(id, this.props.offset);
  }

  componentWillUnmount() {
    const id = this.props.data.get('id');
    this.props.notify_global_offset_changed(
      OffsetChangedType.remove,
      this.props.path,
      this.props.offset,
      compute_text_size(this.props.data.get('name')),
    );
    set_child_offsets(id, undefined);
    set_child_sizes(id, undefined);
    set_node_size(id, undefined);
    set_node_offset(id, undefined);
  }

  child_offsets: Offset[] = [];
  child_sizes: Size[] = [];
  node_size: Size = { width: 0, height: 0 };

  render() {
    const props = this.props;
    const name = props.data.get('name');
    const imgs = props.data.get('imgs');
    const comment = props.data.get('comment');
    const children = props.data.get('children');
    const important = props.data.get('important');

    const node_text_size = compute_text_size(name);
    const { size: node_size, child_sizes } = compute_node_size(props.data);
    const child_offsets = [];
    for (let i = 0, o = -node_size.height / 2; i < child_sizes.length; ++i) {
      const offset: Offset = {
        x: DIS_PARENT_CHILDREN * props.scale,
        y:
          o +
          i * MIN_DIS_SIB * props.scale +
          child_sizes[i].height / 2 -
          node_text_size.height,
      };

      child_offsets.push(offset);
      o += child_sizes[i].height;
    }
    this.child_offsets = child_offsets;
    this.child_sizes = child_sizes;
    this.node_size = node_size;

    return (
      <div
        className={styles.node}
        style={{
          left: props.offset.x,
          top: props.offset.y,
          height: node_text_size.height,
          borderColor: important ? '#ff6666' : '#2196f3',
        }}
        id={props.data.get('id')}
        {...Object.keys(props.events).reduce((m, i) => {
          m[i] = (e) => {
            props.events[i](props.path, e);
          };
          return m;
        }, {})}
        onTouchStart={(e) => {
          const touchmove_listener = (_e) =>
            props.events.onTouchMove(props.path, _e);
          const target = e.target;
          const touchend_listener = (_e) => {
            target.removeEventListener('touchend', touchend_listener);
            target.removeEventListener('touchmove', touchmove_listener);
            props.events.onTouchEnd(props.path, _e);
          };
          target.addEventListener('touchend', touchend_listener);
          target.addEventListener('touchmove', touchmove_listener);
          props.events.onTouchStart(props.path, e);
        }}
      >
        <div
          className={styles.btnNote}
          style={{
            display: comment ? 'block' : 'none',
            left: node_text_size.width - NODE_TEXT_PADDING * 2 + 8,
          }}
          onClick={() => {
            props.comment_on_click(props.path);
          }}
        >
          备注
          <div className={styles.noteArea}>{comment}</div>
        </div>
        <div
          className={styles.nodeText}
          style={{
            width: node_text_size.width - NODE_TEXT_PADDING * 2 + 1,
            height: node_text_size.height,
          }}
        >
          {name}
        </div>
        {imgs.size ? (
          <div className={styles.imgStack} style={{ height: 20 * props.scale }}>
            {imgs.map((i, index) => (
              <img
                className={styles.img}
                key={i.get('id')}
                src={i.get('src')}
                onClick={(e) => {
                  e.stopPropagation();
                  props.img_on_click(props.path, index, e);
                }}
                {...img_stop_propagation_events}
              />
            ))}
          </div>
        ) : null}
        {props.is_root ? null : (
          <NodePath
            offset={{
              x: props.offset.x,
              y:
                props.offset.y +
                (props.offset.y !== 0 ? node_text_size.height : 0),
            }}
          />
        )}
        <div
          style={{
            position: 'absolute',
            bottom: 0,
            left: node_text_size.width,
          }}
        >
          {(children || []).map((i, index) => (
            <MindNode
              scale={props.scale}
              notify_global_offset_changed={props.notify_global_offset_changed}
              img_on_click={props.img_on_click}
              comment_on_click={props.comment_on_click}
              path={[...props.path, 'children', index]}
              key={i.get('id')}
              data={i}
              offset={child_offsets[index]}
              events={props.events}
            />
          ))}
        </div>
      </div>
    );
  }
}
